from threading import Thread, Condition
from threading import Lock as Lock
from typing import Callable
import time
class Fsm:
    '''有限状态机'''
    def __init__(self, ):
        self.Lock = Lock()
        self.Cond = Condition(self.Lock)
        self.State = 'Stop'
    def setState(self, State: str):
        with self.Lock:
            self.State = State
            self.Cond.notify_all()
    
    def getState(self):
        with self.Lock:
            State = self.State
        return State
    
    def waitForChange(self):
        '''利用信号量进行等待'''
        with self.Lock:
            self.Cond.wait()

class Th:
    '''可控线程组'''
    def __init__(self, Wait: Callable, Start: Callable, Run: Callable, Join: Callable):
        '''传入的回调函数应该输入一个Fsm对象'''
        self.Wait = Wait
        self.Start = Start
        self.Run = Run
        self.Join = Join
        self.Fsm = Fsm()
    
    def _main(self):
        '''线程主函数'''
        while self.Fsm.getState() != 'Stop':
            while self.Fsm.getState() == 'Wait':
                self.Wait(self.Fsm)
            if self.Fsm.getState() == 'Start':
                self.Start(self.Fsm)
            while self.Fsm.getState() == 'Run':
                self.Run(self.Fsm)
            self.Join(self.Fsm)

    def crt(self):
        self.Thread = Thread(target=self._main)
        self.Thread.start()
    
    def start(self):
        self.Fsm.setState('Start')
    
    def stop(self):
        self.Fsm.setState('Stop')
    
    def join(self):
        if hasattr(self, 'Thread'):
            self.Thread.join()
        else:
            raise Exception('Thread not created')

    # with支持
    def __enter__(self):
        self.crt()
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.stop()
        self.join()

if __name__ == '__main__':
    def wait(fsm: Fsm):
        print('wait')
        fsm.waitForChange()
    def start(fsm: Fsm):
        print('start')
        fsm.setState('Run')
    def run(fsm: Fsm):
        print('run')
        time.sleep(1)
    def join(fsm: Fsm):
        print('join')
    
    with Th(wait, start, run, join) as th:
        time.sleep(2)